package org.mysterylab.utopiaframework.util.security;

import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 支持 SHA-1 / MD5 消息摘要的工具类, 支持 HEX 与 Base64 两种编码方式.
 * 
 * @author zhouych
 * @see EncoderUtil
 */
public class DigestUtil {
	
	private static Logger logger = LoggerFactory.getLogger(DigestUtil.class);

	/**
	 * SHA-1 散列算法
	 */
	public static final String SHA1 = "SHA-1";
	
	/**
	 * MD5 散列算法
	 */
	public static final String MD5 = "MD5";
	
	/**
	 * MD5 加密需要用到的字符串
	 */
	private static char[] NUMBERS = new char[]{
		'0', '1', '2', '3', '4', '5',
		'6', '7', '8', '9', 'A', 'B',
		'C', 'D', 'E', 'F'
	};
	
	/**
	 * 将输入的字符串生成 32 位的 MD5 值.
	 * 
	 * @param input
	 * @return
	 * @throws NoSuchAlgorithmException
	 */
	public static String getMD5String(String input) throws NoSuchAlgorithmException {
		
		if (input == null) {
			throw new NullPointerException("there is no input text.");
		}
		// 32 位的 MD5 值
		char[] output = new char[32];
		MessageDigest md = MessageDigest.getInstance("MD5");
		byte[] tmp = md.digest(input.getBytes());
		for (int i = 0; i < tmp.length; i++) {
			output[2 * i] = NUMBERS[tmp[i] & 0xF0 >> 4];
			output[2 * i + 1] = NUMBERS[tmp[i] & 0xF];
		}
		return new String(output);
	}
	
	/**
	 * 对输入的字符串进行 SHA 散列, 返回其 Hex 编码的结果.
	 * 
	 * @param input
	 * @return
	 * @throws NoSuchAlgorithmException
	 */
	public static String shalToHex(String input) throws NoSuchAlgorithmException {
		byte[] digestResult = digest(input, SHA1);
		return EncoderUtil.hexEncode(digestResult);
	}
	
	/**
	 * 对输入的字符串进行 SHA 散列, 返回其 Base64 编码的结果.
	 * 
	 * @param input
	 * @return
	 * @throws NoSuchAlgorithmException
	 */
	public static String shalToBase64(String input) throws NoSuchAlgorithmException {
		byte[] digestResult = digest(input, SHA1);
		return EncoderUtil.base64Encode(digestResult);
	}
	
	/**
	 * 对输入的字符串进行 SHA 散列, 返回其 Base64 编码的 URL 安全的结果.
	 * 
	 * @param input
	 * @return
	 * @throws NoSuchAlgorithmException
	 */
	public static String shalToBase64UrlSafe(String input)
			throws NoSuchAlgorithmException {
		byte[] digestResult = digest(input, SHA1);
		return EncoderUtil.base64UrlSafeEncode(digestResult);
	}
	
	/**
	 * 对文件进行 SHA 散列, 返回其 Hex 编码的结果.
	 * 
	 * @param input
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws IOException
	 */
	public static String shalToHex(InputStream input)
			throws NoSuchAlgorithmException, IOException {
		return digest(input, SHA1);
	}
	
	/**
	 * 对文件进行散列, 支持 SHA1 和 MD5 算法.
	 * 
	 * @param input
	 * @param algorithm
	 * @return
	 * @throws NoSuchAlgorithmException 
	 * @throws IOException 
	 */
	private static String digest(InputStream input, String algorithm)
			throws NoSuchAlgorithmException, IOException {

		MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
		int bufferSize = 1024;
		byte[] buffer = new byte[bufferSize];
		int read = input.read(buffer, 0, bufferSize);
		while (read > -1) {
			messageDigest.update(buffer, 0, read);
			read = input.read(buffer, 0, bufferSize);
		}
		return EncoderUtil.hexEncode(messageDigest.digest());
	}
	
	/**
	 * 对字符串进行散列, 支持 SHA1 和 MD5 算法.
	 * 
	 * @param input
	 * @param algorithm
	 * @return
	 * @throws NoSuchAlgorithmException 
	 * @throws NoSuchAlgorithmException
	 */
	private static byte[] digest(String input, String algorithm)
			throws NoSuchAlgorithmException {
		
		return MessageDigest.getInstance(algorithm).digest(input.getBytes());
	}
}
